package com.jzo2o.health.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.RefundRecordApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.constants.MqConstants;
import com.jzo2o.common.constants.UserType;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.*;
import com.jzo2o.health.constant.RedisConstants;
import com.jzo2o.health.constant.TradeConstants;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.mapper.OrdersMapper;
import com.jzo2o.health.model.UserThreadLocal;
import com.jzo2o.health.model.domain.*;
import com.jzo2o.health.model.dto.OrderCountDTO;
import com.jzo2o.health.model.dto.request.OrdersCancelReqDTO;
import com.jzo2o.health.model.dto.request.OrdersPageQueryReqDTO;
import com.jzo2o.health.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.health.model.dto.response.*;
import com.jzo2o.health.properties.TradeProperties;
import com.jzo2o.health.service.*;
import com.jzo2o.mysql.utils.PageUtils;
import com.jzo2o.rabbitmq.client.RabbitClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author li
 * @since 2024-12-14
 */
@Service
@Slf4j
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersService {

    @Resource
    private IOrdersCancelledService ordersCancelledService;
    @Resource
    private IOrdersRefundService ordersRefundService;
    @Resource
    private OrdersMapper ordersMapper;

    /**
     * @param ordersStatus 订单状态 0：未支付，100：待体检，200：已体检，300：已关闭，400：已取消
     * @param sortBy       排序字段
     * @return java.util.List<com.jzo2o.health.model.dto.response.OrdersResDTO>
     * @description: 用户端滚动分页查询订单
     **/
    @Override
    public List<OrdersResDTO> pageQuery(Integer ordersStatus, Long sortBy) {
        List<Orders> list = this.lambdaQuery().eq(ordersStatus != null, Orders::getOrderStatus, ordersStatus).orderByDesc(sortBy != null, Orders::getSortBy).list();
        if (CollUtils.isEmpty(list)) {
            return CollUtils.emptyList();
        }
        return BeanUtils.copyToList(list, OrdersResDTO.class);
    }

    /**
     * @param id 订单id
     * @return com.jzo2o.health.model.dto.response.OrdersDetailResDTO
     * @description: 根据id查询订单详情
     **/
    @Override
    public OrdersDetailResDTO detail(Long id) {
        if (id == null) {
            throw new BadRequestException("非法订单id");
        }
        Orders orders = this.lambdaQuery().eq(Orders::getId, id).one();
        if (orders == null) {
            throw new BadRequestException("订单不存在");
        }
        //返回结果
        OrdersDetailResDTO ordersDetailResDTO = BeanUtils.copyBean(orders, OrdersDetailResDTO.class);

        //已取消订单信息
        OrdersCancelled ordersCancelled = ordersCancelledService.getById(id);
        if (ObjectUtils.isNotNull(ordersCancelled)) {
            ordersDetailResDTO.setCancelReason(ordersCancelled.getCancelReason());
            ordersDetailResDTO.setCancelTime(ordersCancelled.getCancelTime());
        }
        return ordersDetailResDTO;
    }

    /**
     * @param dto
     * @return com.jzo2o.common.model.PageResult<com.jzo2o.health.model.dto.response.OrdersResDTO>
     * @description: 管理端分页查询订单
     **/
    @Override
    public PageResult<OrdersResDTO> pageQuery(OrdersPageQueryReqDTO dto) {
        // 1. 获取分页对象
        Page<Orders> ordersPage = PageUtils.parsePageQuery(dto, Orders.class);
        // 2. 执行内置分页方法
        LambdaQueryWrapper<Orders> wrapper = Wrappers.<Orders>lambdaQuery()
                .orderByDesc(Orders::getCreateTime)
                .eq(dto.getOrderStatus() != null, Orders::getOrderStatus, dto.getOrderStatus())
                .eq(StringUtils.isNotBlank(dto.getMemberPhone()), Orders::getMemberPhone, dto.getMemberPhone());
        Page<Orders> page = this.page(ordersPage, wrapper);
        // 3. 封装分页结果
        return PageUtils.toPage(page, OrdersResDTO.class);
    }

    /**
     * @return com.jzo2o.health.model.dto.response.OrdersCountResDTO
     * @description: 管理端根据状态统计订单数量
     **/
    @Override
    public OrdersCountResDTO countByStatus() {
        OrdersCountResDTO dto = new OrdersCountResDTO();
        List<OrderCountDTO> orderCountDTOS = ordersMapper.countByStatus();
        orderCountDTOS.forEach(orderCountDTO -> {
            if (ObjectUtils.equal(orderCountDTO.getOrderStatus(), OrderStatusEnum.CANCELLED.getStatus())) {
                dto.setCancelledCount(orderCountDTO.getCount());
            } else if (ObjectUtils.equal(orderCountDTO.getOrderStatus(), OrderStatusEnum.NO_PAY.getStatus())) {
                dto.setNoPayCount(orderCountDTO.getCount());
            } else if (ObjectUtils.equal(orderCountDTO.getOrderStatus(), OrderStatusEnum.WAITING_CHECKUP.getStatus())) {
                dto.setWaitingCheckupCount(orderCountDTO.getCount());
            } else if (ObjectUtils.equal(orderCountDTO.getOrderStatus(), OrderStatusEnum.COMPLETED_CHECKUP.getStatus())) {
                dto.setCompletedCheckupCount(orderCountDTO.getCount());
            } else {
                dto.setClosedCount(orderCountDTO.getCount());
            }
            Integer count = this.lambdaQuery().count();
            dto.setTotalCount(count);
        });
        return dto;
    }

    /**
     * @param id 订单id
     * @return com.jzo2o.health.model.dto.response.AdminOrdersDetailResDTO
     * @description: 管理端根据id查询订单详情
     **/
    @Override
    public AdminOrdersDetailResDTO aggregation(Long id) {
        if (id == null) {
            throw new BadRequestException("非法订单id");
        }
        Orders orders = this.getById(id);
        if (orders == null) {
            throw new BadRequestException("订单不存在");
        }

        //封装orderInfo
        AdminOrdersDetailResDTO.OrderInfo orderInfo = BeanUtils.copyBean(orders, AdminOrdersDetailResDTO.OrderInfo.class);

        //封装payInfo
        AdminOrdersDetailResDTO.PayInfo payInfo = BeanUtils.copyBean(orders, AdminOrdersDetailResDTO.PayInfo.class);
        payInfo.setThirdOrderId(orders.getTransactionId());
        //封装cancelInfo
        OrdersCancelled ordersCancelled = ordersCancelledService.getById(id);
        AdminOrdersDetailResDTO.CancelInfo cancelInfo = new AdminOrdersDetailResDTO.CancelInfo();
        if (ObjectUtils.isNotNull(ordersCancelled)) {
            cancelInfo = BeanUtils.copyBean(ordersCancelled, AdminOrdersDetailResDTO.CancelInfo.class);
        }

        //封装refundInfo
        AdminOrdersDetailResDTO.RefundInfo refundInfo = BeanUtils.copyBean(orders, AdminOrdersDetailResDTO.RefundInfo.class);
        refundInfo.setRefundStatus(orders.getPayStatus());
        refundInfo.setCancelTime(ordersCancelled.getCancelTime());
        refundInfo.setCancelReason(ordersCancelled.getCancelReason());
        return new AdminOrdersDetailResDTO(orderInfo, payInfo, refundInfo, cancelInfo);
    }

    /**
     * @param dto
     * @return com.jzo2o.health.model.dto.response.PlaceOrderResDTO
     * @description: 用户端预约下单
     **/

    @Resource
    private ISetmealService setmealService;
    @Resource
    private IMemberService memberService;
    @Resource
    RabbitClient rabbitClient;

    @Override
    public PlaceOrderResDTO place(PlaceOrderReqDTO dto) {
        //订单id
        Long orderId = generateOrderId();
        // 封装订单信息--体检人信息、套餐id、预约日期
        Orders orders = BeanUtils.copyBean(dto, Orders.class);
        // 1. 封装订单基本信息
        orders.setId(orderId);//订单id
        orders.setOrderStatus(OrderStatusEnum.NO_PAY.getStatus());//订单状态 默认待支付
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());//支付状态 默认未支付
        orders.setSortBy(DateUtils.toEpochMilli(LocalDateTime.now()));//排序字段  取创建时间的时间戳

        // 2. 封装订单套餐信息
        Setmeal setmeal = setmealService.getById(dto.getSetmealId());
        orders.setSetmealId(dto.getSetmealId());
        orders.setSetmealImg(setmeal.getImg());
        orders.setSetmealAge(setmeal.getAge());
        orders.setSetmealPrice(BigDecimal.valueOf(setmeal.getPrice()));
        orders.setSetmealName(setmeal.getName());
        orders.setSetmealRemark(setmeal.getRemark());
        orders.setSetmealSex(setmeal.getSex());

        // 3. 封装用户信息
        Member member = memberService.getById(UserThreadLocal.currentUserId());
        orders.setMemberId(member.getId());
        orders.setMemberPhone(member.getPhone());

        // 4. 订单信息插入数据库
        owner.saveOrder(orders);
        // 5. 已预约人数加1
        owner.reserve(dto);

        // 6. 发送消息到延迟交换机(15min)
        rabbitClient.sendMsg(MqConstants.Exchanges.ORDERS,
                MqConstants.RoutingKeys.ORDERS_CANCEL_KEY,
                orders.getId().toString(),
                60000, //方便测试，此处写60s
                IdUtils.getSnowflakeNextId(),
                false);
        //返回订单id
        PlaceOrderResDTO resDTO = new PlaceOrderResDTO();
        resDTO.setId(orderId);
        return resDTO;
    }

    /**
     * 订单信息插入数据库
     *
     * @param orders
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOrder(Orders orders) {
        boolean result = this.save(orders);
        if (!result) {
            throw new DBException("下单失败");
        }
    }

    /**
     * @param dto
     * @description: 已预约人数加1
     **/
    @Transactional(rollbackFor = Exception.class)
    public void reserve(PlaceOrderReqDTO dto) {
        ReservationSetting reservationSetting = reservationSettingService.lambdaQuery()
                .eq(ReservationSetting::getOrderDate, dto.getReservationDate())
                .one();
        boolean resultForReservationSetting = reservationSettingService.lambdaUpdate()
                .set(ReservationSetting::getReservations, reservationSetting.getReservations() + 1)
                .eq(ReservationSetting::getId, reservationSetting.getId())
                .update();
        if (!resultForReservationSetting) {
            throw new DBException("预约失败");
        }
    }

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * @return java.lang.Long
     * @description: 生成订单id 6位日期 + 13位序列号
     **/
    private Long generateOrderId() {
        Long idPrefix = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd");
        Long idSuffix = redisTemplate.opsForValue().increment(RedisConstants.JKTJ_ORDER_ID);
        return idPrefix * 10000000000000L + idSuffix;
    }

    @Resource
    private NativePayApi nativePayApi;
    @Resource
    private TradeProperties tradeProperties;
    @Resource
    private IOrdersService owner;

    /**
     * @param id
     * @param tradingChannel
     * @return com.jzo2o.health.model.dto.response.OrdersPayResDTO
     * @description: 用户端生成支付二维码
     **/
    @Override
    public OrdersPayResDTO pay(Long id, PayChannelEnum tradingChannel) {
        OrdersPayResDTO dto = new OrdersPayResDTO();
        // 1. 判断订单id是否存在
        if (ObjectUtils.isNull(id)) {
            throw new BadRequestException("非法订单id");
        }
        // 2. 判断订单是否存在
        Orders orders = this.getById(id);
        if (ObjectUtils.isNull(orders)) {
            throw new BadRequestException("订单不存在");
        }
        // 3. 如果订单支付成功  直接返回
        if (ObjectUtils.equal(orders.getPayStatus(), OrderPayStatusEnum.PAY_SUCCESS.getStatus()) && orders.getTradingOrderNo() != null) {
            dto.setProductOrderNo(id);
            dto.setTradingOrderNo(orders.getTradingOrderNo());
            dto.setTradingChannel(orders.getTradingChannel());
            dto.setPayStatus(orders.getPayStatus());
            return dto;
        }

        // 4. 订单未支付，远程调用支付服务生成支付二维码
        // 4.1 判断是否切换交易渠道
        boolean changeChannel = false;
        if (ObjectUtils.isNotNull(orders.getTradingChannel()) && ObjectUtils.notEqual(orders.getTradingChannel(), tradingChannel)) {
            changeChannel = true;
        }
        // 4.1 封装请求参数
        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();
        nativePayReqDTO.setTradingChannel(tradingChannel);//交易渠道
        nativePayReqDTO.setTradingAmount(BigDecimal.valueOf(0.01));//交易金额
        //nativePayReqDTO.setTradingAmount(orders.getSetmealPrice());
        nativePayReqDTO.setProductAppId(TradeConstants.PRODUCT_APP_ID);//业务系统标识
        nativePayReqDTO.setMemo(orders.getSetmealName());//备注
        nativePayReqDTO.setProductOrderNo(id);//业务系统订单号

        Long enterpriseId = ObjectUtils.equal(tradingChannel, PayChannelEnum.WECHAT_PAY)
                ? tradeProperties.getWechatEnterpriseId() : tradeProperties.getAliEnterpriseId();
        nativePayReqDTO.setEnterpriseId(enterpriseId); //商户号
        nativePayReqDTO.setChangeChannel(changeChannel);//是否更换支付渠道
        //发起请求获取响应结果
        NativePayResDTO nativePayResDTO = nativePayApi.createDownLineTrading(nativePayReqDTO);
        if (nativePayResDTO == null) {
            throw new BadRequestException("调用支付服务的支付接口失败");
        }
        // 5. 更新订单表数据
        owner.updateOrders(id, nativePayResDTO);
        // 封装返回结果
        BeanUtils.copyProperties(nativePayResDTO, dto);
        return dto;
    }

    /**
     * @param id  订单id
     * @param dto 支付接口响应结果
     * @description: 支付成功，更新订单
     **/
    @Transactional(rollbackFor = Exception.class)
    public void updateOrders(Long id, NativePayResDTO dto) {
        boolean result = this.lambdaUpdate()
                .set(Orders::getTradingOrderNo, dto.getTradingOrderNo())
                .set(Orders::getTradingChannel, dto.getTradingChannel())
                .eq(Orders::getId, id)
                .update();
        if (!result) {
            throw new DBException("支付失败");
        }
    }


    @Resource
    private TradingApi tradingApi;

    /**
     * @param id 订单id
     * @return com.jzo2o.health.model.dto.response.OrdersPayResDTO
     * @description: 查询订单支付结果
     **/
    @Override
    public OrdersPayResDTO payResult(Long id) {
        // 1. 判断订单是否存在
        Orders orders = this.getById(id);
        if (ObjectUtils.isNull(orders)) {
            throw new BadRequestException("订单不存在");
        }
        // 2. 判断交易单号是否存在
        if (ObjectUtils.isNull(orders.getTradingOrderNo())) {
            throw new BadRequestException("非法订单");
        }

        OrdersPayResDTO ordersPayResDTO = BeanUtils.copyBean(orders, OrdersPayResDTO.class);
        ordersPayResDTO.setProductOrderNo(id);

        // 3. 判断订单是否是未支付状态，不是则直接返回
        if (ObjectUtils.notEqual(orders.getPayStatus(), OrderPayStatusEnum.NO_PAY.getStatus())) {
            return ordersPayResDTO;
        }
        // 4. 调用支付服务接口，获取支付结果
        TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());

        // 5. 更新订单表信息（支付时间、第三方交易号、订单状态、支付状态）
        if (tradingResDTO != null && ObjectUtils.equal(tradingResDTO.getTradingState(), TradingStateEnum.YJS)) {
            owner.paySuccess(id, tradingResDTO.getTransactionId(), tradingResDTO.getPaySuccessTime());
            ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
            ordersPayResDTO.setQrCode(tradingResDTO.getQrCode());
        }
        return ordersPayResDTO;
    }

    /**
     * @param orderId       订单id
     * @param transactionId 第三方交易号id
     * @param payTime       支付时间
     * @description: 支付成功更新订单表信息
     **/
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(Long orderId, String transactionId, LocalDateTime payTime) {
        boolean result = this.lambdaUpdate()
                .set(Orders::getTransactionId, transactionId)
                .set(Orders::getPayTime, payTime)
                .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                .set(Orders::getOrderStatus, OrderStatusEnum.WAITING_CHECKUP.getStatus())
                .eq(Orders::getId, orderId)
                .update();
        if (!result) {
            throw new DBException("更新订单状态失败");
        }
    }

    @Resource
    private IReservationSettingService reservationSettingService;

    /**
     * @param dto
     * @description: 用户端取消未支付订单
     **/
    @Override
    public void cancel(OrdersCancelReqDTO dto, boolean isSystem) {
        // 1. 判断订单是否存在
        Orders orders = this.getById(dto.getId());
        if (ObjectUtils.isNull(orders)) {
            throw new BadRequestException("订单不存在");
        }
        // 2. 更新订单表信息
        boolean resultForOrder = this.lambdaUpdate()
                .set(Orders::getOrderStatus, OrderStatusEnum.CANCELLED.getStatus())
                .eq(Orders::getId, dto.getId())
                .update();
        if (!resultForOrder) {
            throw new DBException("更新订单表失败");
        }
        // 3. 新增orders_cancelled数据
        OrdersCancelled ordersCancelled = new OrdersCancelled();
        ordersCancelled.setId(dto.getId());
        if (!isSystem) {
            ordersCancelled.setCancellerId(UserThreadLocal.currentUserId());
            ordersCancelled.setCancellerName(UserThreadLocal.currentUser().getName());
            ordersCancelled.setCancellerType(UserType.C_USER);
        }
        ordersCancelled.setCancelReason(dto.getCancelReason());
        ordersCancelled.setCancelTime(LocalDateTime.now());

        owner.saveOrderCancel(ordersCancelled);

        // 4. 预约人数减一
        owner.decreReservations(orders);
    }

    /**
     * @param orders
     * @description: 已预约人数减一
     **/
    @Transactional(rollbackFor = Exception.class)
    public void decreReservations(Orders orders) {
        ReservationSetting reservationSetting = reservationSettingService.lambdaQuery()
                .eq(ReservationSetting::getDate, orders.getReservationDate())
                .one();
        boolean result = reservationSettingService.lambdaUpdate()
                .set(ReservationSetting::getReservations, reservationSetting.getReservations() - 1)
                .eq(ReservationSetting::getId, reservationSetting.getId())
                .update();
        if (!result) {
            throw new DBException("恢复预约人数失败");
        }
    }

    /**
     * @param ordersCancelled
     * @description: 保存订单取消数据
     **/
    @Transactional(rollbackFor = Exception.class)
    public void saveOrderCancel(OrdersCancelled ordersCancelled) {
        boolean resultForCancel = ordersCancelledService.save(ordersCancelled);
        if (!resultForCancel) {
            throw new DBException("新增订单取消数据失败");
        }
    }

    @Resource
    private RefundRecordApi refundRecordApi;


    /**
     * @param ordersRefund
     * @description: 调用api退款
     **/
    /*
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderRefund(OrdersRefund ordersRefund) {
        // 1. 调用支付服务的feign接口发起退款申请
        Integer refundStatus = RefundStatusEnum.APPLY_REFUND.getCode();
        ExecutionResultResDTO refundResDTO = null;
        try {
            refundResDTO = refundRecordApi.refundTrading(ordersRefund.getTradingOrderNo(), ordersRefund.getRealPayAmount());
            //如果退款后仍是退款中状态，隔3s后再退一次
            if (ObjectUtils.equal(refundResDTO.getRefundStatus(), RefundStatusEnum.SENDING.getCode())) {
                TimeUnit.SECONDS.sleep(3);
                refundResDTO = refundRecordApi.refundTrading(ordersRefund.getTradingOrderNo(), ordersRefund.getRealPayAmount());
                refundStatus = refundResDTO.getRefundStatus();
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new BadRequestException("[退款操作]调用支付服务退款接口失败，待退款记录信息：" + ordersRefund);
        }
        if (refundResDTO == null) {
            throw new BadRequestException("[退款操作]调用支付服务退款接口失败，待退款记录信息：" + ordersRefund);
        }
        // 2. 更新订单表的退款相关字段的值 update orders set refund_status=?,refund_no=?,refund_id=? where id=?
        boolean resultForOrder = this.lambdaUpdate()
                .set(Orders::getPayStatus,
                        ObjectUtils.equal(refundStatus, RefundStatusEnum.SUCCESS.getCode())
                                ? OrderPayStatusEnum.REFUND_SUCCESS.getStatus()
                                : OrderPayStatusEnum.REFUND_FAIL.getStatus())
                .set(Orders::getRefundNo, refundResDTO.getRefundNo())
                .set(Orders::getRefundId, refundResDTO.getRefundId())
                .eq(Orders::getId, ordersRefund.getId())
                .update();
        if (!resultForOrder) {
            throw new DBException("[退款操作]更新订单表失败，待退款记录信息：+ ordersRefund");
        }
        // 3. 删除待退款记录
        boolean resultForRefund = ordersRefundService.removeById(ordersRefund.getId());
        if (!resultForRefund) {
            throw new DBException("[退款操作]删除待退款记录失败，待退款记录信息：" + ordersRefund);
        }

        log.info("[退款处理]退款申请及结果处理成功，待退款记录信息：{}，最终退款状态：{}", ordersRefund, refundStatus);
    }
    */
}
